package com.xnx3.j2ee.util;

import com.xnx3.j2ee.entity.Permission;
import com.xnx3.j2ee.entity.User;
import com.xnx3.j2ee.pojo.bo.ActiveUserBo;
import com.xnx3.j2ee.pojo.bo.PermissionMarkBo;
import com.xnx3.j2ee.pojo.bo.PermissionTreeBo;
import com.xnx3.j2ee.shiro.ShiroFunc;
import com.xnx3.j2ee.util.cache.RedisUtil;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.ExcessiveAttemptsException;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.LockedAccountException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.session.Session;
import org.apache.shiro.session.mgt.DefaultSessionKey;
import org.apache.shiro.session.mgt.SessionKey;
import org.apache.shiro.session.mgt.SimpleSession;
import org.apache.shiro.subject.SimplePrincipalCollection;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.subject.support.DefaultSubjectContext;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.springframework.beans.BeanUtils;

import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

//import com.google.gson.Gson;

/**
 * session 操作
 * @author Hare
 *
 */
public class SessionUtil extends ShiroFunc {
	
	//是否允许百度UEDITOR上传图片、文件。 true允许，false不允许
	public static final String PLUGIN_NAME_UEDITOR_ALLOW_UPLOAD = "system:ueditor:allowUploadForUEditor";
	//UEditor上传文件相关的参数，可用{uploadParam1}来调用。每个人的上传参数都会不同
	public static final String PLUGIN_NAME_UEDITOR_UPLOAD_PARAM_1 = "system:ueditor:ueUploadParam1";
	//当前使用哪个语言包
	public static final String PLUGIN_NAME_LANGUAGE_PACKAGE_NAME = "system:languagePackageName";
	
	/**
	 * 从Shrio的Session中获取当前用户的缓存信息
	 */
//	public static UserBean getUserBeanForSession(){
//		return getUserBeanForShiroSession();
//	}
	//如果使用redis，那么将每个用户的 activeUser.getPluginMap() 作为value存入到redis的key，如 plugin_map_userid_1
	public static final String REDIS_PLUGIN_MAP_NAME = "com.xnx3.j2ee.bean.ActiveUser.plugin.userid";
	public static final int REDIS_PLUGIN_MAP_EXPIRETIME = 2*60*60*24;	//pluginMap 在redis中存在的有效期是2天
	
	/**
	 * 获取当前用户是否已经登录
	 * @return true:已登录；  false:未登录
	 */
	public static boolean isLogin() {
		Subject subject = SecurityUtils.getSubject();
		//取身份信息
		return subject.getPrincipal() != null;
	}

	/**
	 * 从Shrio的Session中获取当前用户的缓存信息
	 */
	public static ActiveUserBo getActiveUser() {
		if (SecurityUtils.getSubject() == null) {
			return null;
		} else {
			Subject subject = SecurityUtils.getSubject();
			//取身份信息
			ActiveUserBo activeUserBo = null;
			Object obj = subject.getPrincipal();
			if (obj == null) {
				return null;
			}
			if (SystemUtil.isJarRun()) {
				//jar包方式运行，可能是开发环境
				if (obj instanceof ActiveUserBo) {
					activeUserBo = (ActiveUserBo) obj;
				} else {
//					Gson gson = new Gson();
					activeUserBo = new ActiveUserBo();
					BeanUtils.copyProperties(obj, activeUserBo);
					ConsoleUtil.debug("fuzhi -- "+ activeUserBo.toString());
//					activeUser = gson.fromJson(gson.toJson(obj), ActiveUser.class);
				}
			} else {
				//部署到服务器了
				activeUserBo = (ActiveUserBo) obj;
			}
			
			if (activeUserBo != null) {
				return activeUserBo;
			} else {
				return null;
			}
		}
	}
	
	/**
	 * 从Shrio的Session中，获取指定SessionId的用户的缓存信息
	 * @param token 也就是SessionId
	 * @return 获取到的 {@link ActiveUserBo} 如果当前tokne没有对应的信息，也就是未登录、或者假token，那么返回null
	 */
	public static ActiveUserBo getActiveUser(String token) {
		if (CacheUtil.isUseRedis()) {
			Object obj = CacheUtil.get("shiro:session:"+token);
			if (obj != null) {
				org.apache.shiro.session.mgt.SimpleSession s = (SimpleSession) obj;
				org.apache.shiro.subject.SimplePrincipalCollection principal = (org.apache.shiro.subject.SimplePrincipalCollection) s.getAttribute("org.apache.shiro.subject.support.DefaultSubjectContext_PRINCIPALS_SESSION_KEY");
				ActiveUserBo activeUserBo = (ActiveUserBo) principal.getPrimaryPrincipal();
				if (activeUserBo != null) {
					return activeUserBo;
				}
			}
			return null;
		} else {
			//不使用redis，那么就走shiro管理的从shiro中取session对应的用户
			SessionKey sessionKey = new DefaultSessionKey(token);
			org.apache.shiro.mgt.SecurityManager manager = SecurityUtils.getSecurityManager();
//			DefaultSecurityManager manager = (DefaultSecurityManager) SecurityUtils.getSecurityManager();
			
			Session se;
			try {
				se = manager.getSession(sessionKey);
			} catch (org.apache.shiro.session.UnknownSessionException e) {
				return null;
			}
			if (se == null) {
				//未登录，没有用户信息
				return null;
			}
			
			Object obj = se.getAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY);
			if (obj == null) {
				return null;
			}
			SimplePrincipalCollection coll = (SimplePrincipalCollection) obj;
			if (coll == null) {
				return null;
			}
			Object p = coll.getPrimaryPrincipal();
			if (p != null) {
				return (ActiveUserBo)p;
			}
			
			return null;
		}
	}
	
	public static void main(String[] args) {
		System.out.println(getActiveUser("s"));
	}
	

	/**
	 * 获取用户Session中缓存的某个插件信息。这里的信息，可以用 {@link #setPlugin(String, Object)} 来设置写入
	 * 这里是从 {@link ActiveUserBo#getPluginMap()} 中取的
	 * 可以从session中获取该用户某个插件的缓存信息，避免频繁查数据库。
	 * 前提是已经将该用户的插件的信息缓存进去了
	 * @param activeUserBo 当前登录的 activeUser 信息
	 * @param key 整个网市场云建站系统中，pluginMap.key,唯一的，具体插件可以为 kefu、cnzz， 网站管理后台缓存网站对象的key可以为 wangmarket_site
	 * @return 如果获取到，返回。如果获取不到，如用户未登录、插件信息不存在，则返回null
	 */
	public static Object getPluginObject(ActiveUserBo activeUserBo, String key) {
		if (activeUserBo == null) {
			return null;
		}
		
		Object obj = null;
//		User user = activeUser.getUser();
//		if (user == null) {
//			//未登陆
//			return null;
//		}
		//判断是从redis中取，还是从本地服务器中取
		if (CacheUtil.isUseRedis()) {
			
			User user = activeUserBo.getUser();
			if (user == null) {
				//未登陆,这个有时候热部署在遇到springmvc拦截器时会遇到异常
				return null;
			}
			
			Object redisObj = CacheUtil.get(REDIS_PLUGIN_MAP_NAME+":"+user.getId());
			if (redisObj == null) {
				return null;
			}
			Map<String, Object> map = (HashMap<String, Object>) redisObj;
			obj = map.get(key);
		} else {
			obj = activeUserBo.getPluginMap().get(key);
		}
		
		if (obj == null) {
			return null;
		}
		return obj;
	}
	
	/**
	 * 获取用户Session中缓存的某个插件信息。这里的信息，可以用 {@link #setPlugin(String, Object)} 来设置写入
	 * 这里是从 {@link ActiveUserBo#getPluginMap()} 中取的
	 * 可以从session中获取该用户某个插件的缓存信息，避免频繁查数据库。
	 * 前提是已经将该用户的插件的信息缓存进去了
	 * @param key 整个网市场云建站系统中，pluginMap.key,唯一的，具体插件可以为 kefu、cnzz， 网站管理后台缓存网站对象的key可以为 wangmarket_site
	 * @return 如果获取到，返回。如果获取不到，如用户未登录、插件信息不存在，则返回null
	 */
	public static <T> T getPlugin(String key) {
		ActiveUserBo activeUserBo = getActiveUser();
		Object obj = getPluginObject(activeUserBo, key);
		if (obj == null) {
			return null;
		}
		return (T)obj;
	}
	
	/**
	 * 获取用户Session中缓存的某个插件信息。这里的信息，可以用 {@link #setPlugin(String, Object)} 来设置写入
	 * 这里是从 {@link ActiveUserBo#getPluginMap()} 中取的
	 * 可以从session中获取该用户某个插件的缓存信息，避免频繁查数据库。
	 * 前提是已经将该用户的插件的信息缓存进去了
	 * @param key 整个网市场云建站系统中，pluginMap.key,唯一的，具体插件可以为 kefu、cnzz， 网站管理后台缓存网站对象的key可以为 wangmarket_site
	 * @return 如果获取到，返回。如果获取不到，如用户未登录、插件信息不存在，则返回null
	 */
	public static Object getPluginObject(String key) {
		ActiveUserBo activeUserBo = getActiveUser();
		return getPluginObject(activeUserBo, key);
	}
	

	/**
	 * 设置用户Session中缓存的某个插件信息。这里设置的，可以用 {@link #getPlugin(String)} 取出来
	 * @param activeUserBo 要设置的已登录用户的 {@link ActiveUserBo}
	 * @param key 唯一key
	 * @param value 存储的信息对象
	 */
	public static void setPlugin(ActiveUserBo activeUserBo, String key, Object value) {
		if (activeUserBo == null) {
			return;
		}
		User user = activeUserBo.getUser();
		if (user == null) {
			//未登陆
			return;
		}
		//判断是存到redis中，还是本地服务器中
		if (RedisUtil.isUse()) {
			Map map = null;
			Object obj = RedisUtil.getObject(REDIS_PLUGIN_MAP_NAME+":"+user.getId());
			if (obj == null) {
				map = new HashMap<String, Object>();
			} else {
				map = (HashMap<String, Object>) obj;
			}
			map.put(key, value);
			RedisUtil.setObject(REDIS_PLUGIN_MAP_NAME+":"+user.getId(), map, REDIS_PLUGIN_MAP_EXPIRETIME);
		} else {
			activeUserBo.getPluginMap().put(key, value);
		}
	}
	
	
	/**
	 * 设置用户Session中缓存的某个插件信息。这里设置的，可以用 {@link #getPlugin(String)} 取出来
	 * @param key 唯一key
	 * @param value 存储的信息对象
	 */
	public static void setPlugin(String key, Object value) {
		ActiveUserBo activeUserBo = getActiveUser();
		setPlugin(activeUserBo, key, value);
	}
	
	/**
	 * 是否允许用户通过富文本编辑器(UEditor)上传文件、图片
	 * @return true：允许上传；  false：不允许上传。 如果之前没设置过，默认是能正常上传的。
	 */
	public static boolean isAllowUploadForUEditor() {
		Object obj = getPluginObject(PLUGIN_NAME_UEDITOR_ALLOW_UPLOAD);
		if (obj == null) {
			return true;
		}
		return (boolean) obj;
	}
	
	/**
	 * 是否允许用户通过富文本编辑器(UEditor)上传文件、图片
	 * @param isAllow true：允许上传；  false：不允许上传。
	 */
	public static void setAllowUploadForUEditor(boolean isAllow) {
		setPlugin(PLUGIN_NAME_UEDITOR_ALLOW_UPLOAD, isAllow);
	}
	
	/**
	 * 获取UEditor上传文件相关的参数，可用{uploadParam1}来调用。每个人的上传参数都会不同
	 * @return 字符串 如果之前没设置过，则默认获取到 “defaultparam1”
	 */
	public static String getUeUploadParam1() {
		String param = getPlugin(PLUGIN_NAME_UEDITOR_UPLOAD_PARAM_1);
		if (param == null) {
			return "defaultparam1";
		}
		return param;
	}
	/**
	 * 设置UEditor上传文件相关的参数，可用{uploadParam1}来调用。每个人的上传参数都会不同
	 * @param param 字符串,路径，也就是文件夹名字
	 */
	public static void setUeUploadParam1(String param) {
		setPlugin(PLUGIN_NAME_UEDITOR_UPLOAD_PARAM_1, param);
	}
	
	/**
	 * 获取当前使用的是哪个语言包
	 * @return 语言包的名字
	 */
	public static String getLanguagePackageName() {
		return getPlugin(PLUGIN_NAME_LANGUAGE_PACKAGE_NAME);
	}
	/**
	 * 设置当前使用的是哪个语言包
	 * @param name 语言包的名字
	 */
	public static void setLanguagePackageName(String name) {
		setPlugin(PLUGIN_NAME_LANGUAGE_PACKAGE_NAME, name);
	}
	
	/**
	 * 推出登录
	 */
	public static void logout() {
		Subject subject = SecurityUtils.getSubject();
		if (subject.isAuthenticated()) {
			int userid = getUserId();
			if (userid > 0) {
//				RedisUtil.delkeyObject(key);
			}
			subject.logout(); // session 会销毁，在SessionListener监听session销毁，清理权限缓存
		}
	}
	

	/**
	 * Session中更新当前登录用户的信息。
	 * 如果当前用户已登陆，则更新；
	 * 如果当前用户未登陆，则自动将传入的 user.username 作为登陆用户。（需要确认 username 在 user 表中是存在的，不然登陆不成功）
	 */
	public static void setUser(User user) {
		ActiveUserBo activeUserBo = SessionUtil.getActiveUser();
		if(activeUserBo == null) {
			UsernamePasswordToken token = new UsernamePasswordToken(user.getUsername(), user.getUsername());
			token.setRememberMe(false);
			Subject currentUser = SecurityUtils.getSubject();
			try {
				currentUser.login(token);
			} catch ( UnknownAccountException uae ) {
				uae.printStackTrace();
			} catch ( IncorrectCredentialsException ice ) {
				ice.printStackTrace();
			} catch ( LockedAccountException lae ) {
				lae.printStackTrace();
			} catch ( ExcessiveAttemptsException eae ) {
				eae.printStackTrace();
			} catch ( org.apache.shiro.authc.AuthenticationException ae ) { 
				ae.printStackTrace();
			}
			
			activeUserBo = SessionUtil.getActiveUser();
			activeUserBo.setId(user.getId());
			activeUserBo.setUser(user);
		}else {
			activeUserBo.setUser(user);
		}
		
	}
	
	/**
	 * 清理掉当前登录用户的session，也就是根据某个用户id，删除这个用户的session，让这个用户下线
	 * @param userid 对应User.id 要删除哪个用户的登录状态，就传入哪个用户的id
	 */
	public static void removeSession(int userid) {
		//获取shiro的sessionManager
		DefaultWebSecurityManager securityManager = (DefaultWebSecurityManager) SecurityUtils.getSecurityManager();
		DefaultWebSessionManager sessionManager = (DefaultWebSessionManager)securityManager.getSessionManager();
		// 4.获取所有已登录用户的session列表
		Collection<Session> sessions = sessionManager.getSessionDAO().getActiveSessions();
	 
		if (sessions.size() > 0) {
			for(Session onlineSession:sessions){
				// 清除当前用户以前登录时保存的session会话
				
				SimplePrincipalCollection principalCollection = (SimplePrincipalCollection) onlineSession.getAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY);
				if (principalCollection == null) {
					continue;
				}
				Object obj = principalCollection.getPrimaryPrincipal();
				if (obj == null) {
					continue;
				}
				ActiveUserBo activeUserBo = (ActiveUserBo)obj;
				User user= activeUserBo.getUser();
				if (user == null) {
					continue;
				}
				if (user.getId() - userid == 0) {
					sessionManager.getSessionDAO().delete(onlineSession);
				}
			}
		}
	}
	

	/**
	 * 获取当前登录用户所显示的shiro权限的菜单的HTML
	 * @return
	 */
	public static String getUserMenuHTML() {
		StringBuffer sb = new StringBuffer();
		ActiveUserBo activeUserBo = SessionUtil.getActiveUser();
		if (activeUserBo == null) {
			return "";
		}
		List<PermissionTreeBo> list = activeUserBo.getPermissionTreeBoList();
		for (int i = 0; i < list.size(); i++) {
			PermissionTreeBo ptree = list.get(i);
			Permission firstPermission = ptree.getPermissionMarkBo().getPermission();
			if (firstPermission.getMenu() != null && firstPermission.getMenu() - 1 == 0) {
				//是菜单，显示
				
				//判断是否有下级
				StringBuffer subSB = new StringBuffer();
				if (ptree.getList() != null) {
					List<PermissionMarkBo> subList = ptree.getList();
					
					subSB.append("<dl class=\"layui-nav-child\">");
					boolean haveSubMenu = false;	//是否有子菜单
					for (int j = 0; j < subList.size(); j++) {
						Permission subPermission = subList.get(j).getPermission();
						if (subPermission.getMenu() != null && subPermission.getMenu() - 1 == 0) {
							//是菜单,那么也加入进去
							subSB.append("<dd id=\""+subPermission.getId()+"\" class=\"twoMenu\"><a id=\""+subPermission.getId()+"_a\" class=\"subMenuItem wm_menu\" href=\""+subPermission.getUrl()+"\">"+subPermission.getName()+"</a></dd>");
							haveSubMenu = true;
						}
					}
					subSB.append("</dl>");
					
					sb.append("<li class=\"layui-nav-item\" id=\""+firstPermission.getId()+"\"><a id=\""+firstPermission.getId()+"_a\" href=\""+(haveSubMenu? "javascript:;":firstPermission.getUrl())+"\" class=\"wm_menu\"><i class=\"layui-icon firstMenuIcon\" id=\""+firstPermission.getPercode()+"_icon\">"+(firstPermission.getIcon().length() > 0 ? firstPermission.getIcon():"")+"</i><span class=\"firstMenuFont\">"+firstPermission.getName()+"</span>"+(haveSubMenu? "<span class=\"layui-nav-more\"></span>":"")+"</a>"); 
					if (haveSubMenu) {
						//有子菜单，加入进去
						sb.append(subSB);
					}
					sb.append("</li>");
				}
			}
		}
		
		return sb.toString();
	}

}
